import { AxiosResponse } from 'axios';
import { RESPONSE_CODE_LOADING, RESPONSE_CODE_ERROR } from './system_constant';
import { CodeType, BdjfResponse, AttributeMapping, SetErrorParams } from './type';


export interface ResponseConstructor<T> {
    code: CodeType;
    msg?: string;
    data?: T;
    successCode?: CodeType;
}

export class  ResponseImpl<T> implements BdjfResponse<T> {
    [key: string]: any;
    public code: CodeType;
    public msg?: string;
    public data?: T|undefined;
    public readonly successCode?: CodeType;
    public _originalData_?: AxiosResponse<T>;

    constructor(res: ResponseConstructor<T>) {
        this.code = res.code;
        this.msg = res.msg;
        this.data = res.data;
        this.successCode = res.successCode;
    }

    public setLoading(data?: T): BdjfResponse<T> {
        this.code = RESPONSE_CODE_LOADING;
        if (data) {
            this.data = data;
        }
        return this;
    }

    get success(): boolean {
        return this.code === this.successCode;
    }

    get isLoading(): boolean {
        return this.code === RESPONSE_CODE_LOADING;
    }


    public update(newRes: BdjfResponse<T>): BdjfResponse<T>  {
        for (const i in newRes) {
            this[i] = newRes[i];
        }
        return this;
    }

    public setError(err: SetErrorParams|string): BdjfResponse<T> {
        if (err instanceof String) {
            this.msg = (err as string);
            this.code = RESPONSE_CODE_ERROR;
        } else {

            this.msg = (err as SetErrorParams).msg;
            this.code = (err as SetErrorParams).code;
        }
        return this;
    }

    public setSuccess(data: any): BdjfResponse<T> {
        this.data = data;
        return this;
    }

}

export function createLoadingResWithCode<T= any>(successCode: CodeType, data?: T) {
    return new ResponseImpl<T>({
        code: RESPONSE_CODE_LOADING,
        data,
        successCode,
    });
}

export function createRes<T= any>(successCode: CodeType, newRes: any, mapping: AttributeMapping) {
    const response = new ResponseImpl<T>({
        code: newRes[mapping.code],
        msg: newRes[mapping.msg],
        data: newRes[mapping.data],
        successCode,
    });

    const hasMapKey = [mapping.code, mapping.msg, mapping.data];

    Object.keys(newRes).filter(function(key: string) {
        return hasMapKey.indexOf(key) < 0;
    }).forEach((key: string) => {
        response[key] = newRes[key];
    });

    return response;
}

export function copyRes<T= any>(oldRes: BdjfResponse<T>) {
    return Object.assign(Object.create(oldRes), oldRes);
}
